// ============================================================================
//
// Copyright (C) 2006-2007 Dengues
//
// Google Group: http://groups.google.com/group/dengues
//
// QQ Group: 24885404
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
// ============================================================================
package org.dengues.reports.editor.design.parts;

import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.dengues.reports.editor.design.handlers.AbstractGuideHandle;
import org.dengues.reports.editor.design.models.AbstractReportsElement;
import org.dengues.reports.editor.design.request.ReportRequest;
import org.dengues.reports.editor.design.utils.Policy;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.MouseEvent;
import org.eclipse.draw2d.MouseMotionListener;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Insets;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.gef.DragTracker;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.LayerConstants;
import org.eclipse.gef.Request;
import org.eclipse.gef.RequestConstants;
import org.eclipse.gef.editparts.AbstractGraphicalEditPart;
import org.eclipse.gef.editparts.LayerManager;
import org.eclipse.gef.requests.SelectionRequest;
import org.eclipse.gef.tools.DragEditPartsTracker;
import org.eclipse.swt.widgets.Display;

/**
 * <p>
 * Abstract super class for all report element editPart
 * </p>
 * 
 * /** Qiang.Zhang.Adolf@gmail.com class global comment. Detailled comment <br/>
 * 
 * $Id: Dengues.epf Qiang.Zhang.Adolf@gmail.com 2008-2-22 qiang.zhang $
 * 
 */
public abstract class ReportElementEditPart extends AbstractGraphicalEditPart implements PropertyChangeListener {

    private static final int DELAY_TIME = 1600;

    private AbstractGuideHandle guideHandle = null;

    private boolean isEdited = false;

    protected Logger logger = Logger.getLogger(ReportElementEditPart.class.getName());

    // private static boolean canDeleteGuide = true;

    /**
     * Constructor
     * 
     * @param model
     */
    public ReportElementEditPart(Object model) {
        super();
        if (Policy.TRACING_EDITPART_CREATE) {
            String[] result = this.getClass().getName().split("\\."); //$NON-NLS-1$
            System.out.println(result[result.length - 1] + " >> Created for " //$NON-NLS-1$
                    + model);
        }
        setModel(model);

    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.gef.editparts.AbstractEditPart#setModel(java.lang.Object)
     */
    @Override
    public void setModel(Object model) {
        super.setModel(model);
        // peer = creatDesignElementHandleAdapter();
    }

    /**
     * perform edit directly when the request is the corresponding type.
     */
    @Override
    public void performRequest(Request request) {
        if (RequestConstants.REQ_OPEN.equals(request.getType()) || ReportRequest.CREATE_ELEMENT.equals(request.getType())) {
            if (isEdited()) {
                return;
            }
            setEdited(true);
            try {
                performDirectEdit();
            } catch (RuntimeException e) {
                logger.log(Level.SEVERE, e.getMessage(), e);
            }
            setEdited(false);
        }
    }

    public void performDirectEdit() {
        // do nothing
    }

    protected boolean isEdited() {
        return isEdited;
    }

    protected void setEdited(boolean isEdited) {
        this.isEdited = isEdited;
    }

    /**
     * Creates the guide handle, default get from parent.
     * 
     * @return
     */
    protected AbstractGuideHandle createGuideHandle() {
        EditPart part = getParent();
        if (part instanceof ReportElementEditPart) {
            return ((ReportElementEditPart) part).getGuideHandle();
        }
        return null;
    }

    protected AbstractGuideHandle getGuideHandle() {
        if (guideHandle == null) {
            guideHandle = interCreateGuideHandle();
        }
        return guideHandle;
    }

    private AbstractGuideHandle interCreateGuideHandle() {
        // if (getParent() instanceof MultipleEditPart) {
        // return ((MultipleEditPart) getParent()).createGuideHandle();
        // }
        return createGuideHandle();
    }

    /**
     * Adds the guide handle to the handle layer.
     * 
     */
    public void addGuideFeedBack() {
        if (guideHandle == null) {
            guideHandle = interCreateGuideHandle();
        }

        if (guideHandle != null && guideHandle != findHandle()) {
            clearGuideHandle();
            getHandleLayer().add(guideHandle);
            guideHandle.invalidate();
            guideHandle.setCanDeleteGuide(true);
        } else if (guideHandle != null && guideHandle == findHandle()) {
            guideHandle.setCanDeleteGuide(false);
        } else if (guideHandle != null) {
            guideHandle.setCanDeleteGuide(true);
        }
    }

    private AbstractGuideHandle findHandle() {
        IFigure layer = getHandleLayer();
        List list = layer.getChildren();
        int size = list.size();

        for (int i = 0; i < size; i++) {
            Object obj = list.get(i);
            if (obj instanceof AbstractGuideHandle) {
                return (AbstractGuideHandle) obj;
            }
        }

        return null;
    }

    protected void clearGuideHandle() {
        IFigure layer = getHandleLayer();
        List list = layer.getChildren();
        List temp = new ArrayList();
        int size = list.size();

        for (int i = 0; i < size; i++) {
            Object obj = list.get(i);
            if (obj instanceof AbstractGuideHandle) {
                temp.add(obj);
            }
        }

        size = temp.size();
        for (int i = 0; i < size; i++) {
            IFigure figure = (IFigure) temp.get(i);
            layer.remove(figure);
        }
    }

    /**
     * Removes the guide handle.
     */
    protected void removeGuideFeedBack() {
        if (guideHandle != null && guideHandle.getParent() == getHandleLayer()) {
            getHandleLayer().remove(guideHandle);
        }
        guideHandle = null;
    }

    /**
     * Removes the guide handle after the specified number of milliseconds.
     */
    public void delayRemoveGuideFeedBack() {
        if (guideHandle != null) {
            guideHandle.setCanDeleteGuide(true);
        }
        Display.getCurrent().timerExec(DELAY_TIME, new Runnable() {

            public void run() {
                if (guideHandle != null && guideHandle.isCanDeleteGuide()) {
                    removeGuideFeedBack();
                }
            }

        });
    }

    private IFigure getHandleLayer() {
        super.getLayer(LayerConstants.HANDLE_LAYER);
        LayerManager manager = (LayerManager) getViewer().getEditPartRegistry().get(LayerManager.ID);
        return manager.getLayer(LayerConstants.HANDLE_LAYER);
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.gef.EditPart#activate()
     */
    @Override
    public void activate() {
        if (isActive())
            return;

        super.activate();

        ((AbstractReportsElement) getModel()).addPropertyChangeListener(this);
        // refreshPageClip();

        getFigure().addMouseMotionListener(new MouseMotionListener.Stub() {

            @Override
            public void mouseEntered(MouseEvent me) {
                addGuideFeedBack();

            }

            @Override
            public void mouseExited(MouseEvent me) {
                delayRemoveGuideFeedBack();
            }

            @Override
            public void mouseHover(MouseEvent me) {
                addGuideFeedBack();
            }

            @Override
            public void mouseMoved(MouseEvent me) {
                addGuideFeedBack();
            }

        });

        getFigure().setFocusTraversable(true);
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.gef.EditPart#deactivate()
     */
    @Override
    public void deactivate() {
        if (!isActive())
            return;
        removeGuideFeedBack();

        super.deactivate();

        // HandleAdapterFactory.getInstance().remove(getModel(), this);
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.gef.editparts.AbstractEditPart#createEditPolicies()
     */
    @Override
    protected abstract void createEditPolicies();

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.gef.EditPart#getDragTracker(org.eclipse.gef.Request)
     */
    @Override
    public DragTracker getDragTracker(Request req) {
        DragEditPartsTracker track = new DragEditPartsTracker(this) {

            /*
             * (non-Javadoc)
             * 
             * @see org.eclipse.gef.tools.SelectEditPartTracker#handleButtonDown(int)
             */
            @Override
            protected boolean handleButtonDown(int button) {
                // if (getCurrentViewer() instanceof DeferredGraphicalViewer) {
                // ((DeferredGraphicalViewer) getCurrentViewer()).initStepDat();
                // }
                return super.handleButtonDown(button);
            }
        };
        return track;
    }

    /**
     * @return display label
     */
    public String getDisplayLabel() {
        return null;
    }

    private boolean isDirty = true;

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.gef.editparts.AbstractEditPart#refreshVisuals()
     */
    @Override
    public final void refreshVisuals() {
        super.refreshVisuals();
        refreshFigure();
        refreshReportChildren(this);
        // added for must repaint
        getFigure().repaint();
    }

    public void refreshReportChildren(ReportElementEditPart parent) {
        List list = parent.getChildren();
        for (int i = 0; i < list.size(); i++) {
            Object part = list.get(i);
            if (part instanceof ReportElementEditPart) {
                // if (((ReportElementEditPart) part).isDelete()) {
                // continue;
                // }
                ((ReportElementEditPart) part).refreshFigure();
                refreshReportChildren((ReportElementEditPart) part);
            }
        }
    }

    public abstract void refreshFigure();

    /**
     * Marks edit part dirty
     * 
     * @param bool
     * @param notifyParent
     */
    public void markDirty(boolean bool) {
        this.isDirty = bool;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.birt.report.designer.core.model.IModelAdaptHelper#getPreferredSize()
     */
    public Dimension getPreferredSize() {
        Dimension size = getFigure().getSize().getCopy();
        return size;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.birt.report.designer.core.model.IModelAdaptHelper#getInsets()
     */
    public Insets getInsets() {
        return new Insets(getFigure().getInsets());
    }

    protected boolean isFigureLeft(Request request) {
        if (!(request instanceof SelectionRequest)) {
            return true;
        }
        SelectionRequest selctionRequest = (SelectionRequest) request;
        Point p = selctionRequest.getLocation();
        // getFigure().translateToAbsolute(p);
        getFigure().translateToRelative(p);
        Point center = getFigure().getBounds().getCenter();
        return center.x >= p.x;
    }

    public void notifyModelChange() {
        if (getParent() != null && getParent() instanceof ReportElementEditPart) {
            ((ReportElementEditPart) getParent()).notifyModelChange();
        }
    }

    /**
     * 
     */
    protected void contentChange(Map info) {
        markDirty(true);
        refresh();
    }

    /**
     * @param focus
     */
    protected void propertyChange(Map info) {
        refreshVisuals();
    }

    /**
     * @param model
     * @return
     */
    public boolean isinterest(Object model) {
        return getModel().equals(model);
    }

    /**
     * @param object
     * @return
     */
    public boolean isinterestSelection(Object object) {
        return false;
    }

    public String getGuideLabel() {
        return "";
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "isDelete".
     * 
     * @return
     */
    public boolean isDelete() {
        return false;
    }
}